package com.boot.mall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.boot.common.to.es.SkuEsModel;
import com.boot.mall.search.config.ElasticSearchConfig;
import com.boot.mall.search.constant.EsConstant;
import com.boot.mall.search.service.ProductSearchService;
import com.boot.mall.search.vo.SearchParam;
import com.boot.mall.search.vo.SearchResult;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ProductSearchServiceImpl implements ProductSearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    //在ES中检索商品信息
    @Override
    public SearchResult search(SearchParam searchParam) {
        //1.准备检索请求
        SearchRequest searchRequest = buildSearchRequest(searchParam);
        //2.执行检索请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(searchRequest, ElasticSearchConfig.getCommonOptions());
        } catch (IOException e) {
            e.printStackTrace();
        }
        //3.分析响应结果并且封装我们想要的数据
        SearchResult result = buildSearchResponse(response,searchParam);
        return result;
    }


    // TODo 还没写完呢
    //构建结果数据
    private SearchResult buildSearchResponse(SearchResponse response,SearchParam param) {

        SearchResult result = new SearchResult();

        // 1.1 封装查询的所有sku商品信息
        SearchHits hits = response.getHits();   //命中的数据
        List<SkuEsModel> skuEsModels = new ArrayList<>();
        if (hits.getHits() != null && hits.getHits().length > 0){
            for (SearchHit hit : hits.getHits()) {  //遍历
                String source = hit.getSourceAsString();    //商品的源数据
                SkuEsModel sku = JSON.parseObject(source, SkuEsModel.class);
                //设置高亮
                if (!StringUtils.isEmpty(param.getKeyword())){
                    HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
                    String title = skuTitle.getFragments()[0].string();
                    sku.setSkuTitle(title);
                }
                skuEsModels.add(sku);
            }
        }
        result.setProducts(skuEsModels);

        // 1.2 封装所有商品的属性信息
        ParsedNested attr_agg = response.getAggregations().get("attr_agg");
        ParsedLongTerms attrIdAgg = attr_agg.getAggregations().get("attrIdAgg");
        List<SearchResult.AttrVo> attrVos = new ArrayList<>();
        for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();    //创建属性对象
            //获取attrId聚合的信息
            attrVo.setAttrId(bucket.getKeyAsNumber().longValue());  //设置属性id
            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
            //获取attrName聚合的信息
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
            attrVo.setAttrName(attrName);   //设置属性名称
            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
            //获取attrValue聚合的信息
            List<String> attrs = attrValueAgg.getBuckets().stream().map(item -> {
                String keyAsString = ((Terms.Bucket) item).getKeyAsString();
                return keyAsString;
            }).collect(Collectors.toList());

            attrVo.setAttrValue(attrs);
            attrVos.add(attrVo);
        }
        result.setAttrs(attrVos);

        // 1.3 封装所有商品的分类信息
        ParsedLongTerms catalogAgg = response.getAggregations().get("catalogAgg");
        List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
        //遍历商品分类的bucket
        for (Terms.Bucket bucket : catalogAgg.getBuckets()) {
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();    //创建分类对象
            //获取分类id的聚合
            String catId = bucket.getKeyAsString();
            catalogVo.setCatalogId(Long.parseLong(catId));  //设置分类id
            ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalogNameAgg");
            //获取分类名称的聚合
            String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
            catalogVo.setCatalogName(catalogName);  //设置分类名称
            catalogVos.add(catalogVo);
        }
        result.setCatalogs(catalogVos);

        // 1.4 封装商品的品牌信息
        //获取品牌的聚合信息
        ParsedLongTerms brandAgg = response.getAggregations().get("brandAgg");
        List<SearchResult.BrandVo> brandVos = new ArrayList<>();
        for (Terms.Bucket bucket : brandAgg.getBuckets()) {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            //获取品牌的id
            String brandId = bucket.getKeyAsString();
            brandVo.setBrandId(Long.parseLong(brandId));    //设置品牌id
            //获取品牌图片的聚合信息
            ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brandImgAgg");
            String brandImg = brandImgAgg.getBuckets().get(0).getKeyAsString();
            System.out.println(brandImg);
            brandVo.setBrandImg(brandImg);  //设置品牌地址
            //获取品牌名称的聚合信息
            ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brandNameAgg");
            String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
            brandVo.setBrandName(brandName); //设置品牌名称

            brandVos.add(brandVo);
        }
        result.setBrands(brandVos);

        // 1.5 封装页码信息
        result.setPageNum(param.getPageNum());
        // 1.6 封装总记录数
        long total = response.getHits().getTotalHits().value;
        result.setTotal(total);
        // 1.7 封装总页码数
        int totalPage = (int)(total - 1) / EsConstant.PRODUCT_PAGESIZE + 1;
        result.setTotalPage(totalPage);

        List<Integer> pageNavs = new ArrayList<>();
        for (int i = 1; i <= totalPage ; i++) {
            pageNavs.add(i);
        }
        result.setPageNavs(pageNavs);

        // 1.8 构建面包屑导航功能
        if (param.getAttrs() != null && param.getAttrs().size() > 0){
            List<SearchResult.NavVo> navVos = new ArrayList<>();
            List<SearchResult.AttrVo> attrs = result.getAttrs();
            for (SearchResult.AttrVo attr : attrs) {
                SearchResult.NavVo navVo = new SearchResult.NavVo();
                String attrName = attr.getAttrName();
                navVo.setNavName(attrName);
                navVo.setNavValue(attr.getAttrValue().toString());
                navVos.add(navVo);
            }
            result.setNavVos(navVos);
        }

        return result;
    }

    //构建检索请求
    private SearchRequest buildSearchRequest(SearchParam param) {
        System.out.println(param);
        //构建DSL语句
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        /**
         *  查询：过滤  -->  (属性->分类->品牌->价格区间->库存)
         */
        /*-------------------------------------查询begin--------------------------------------*/
        //1.构建bool - query
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        sourceBuilder.query(boolQuery);
        // 1.1 must - 根据关键字匹配
        if (!StringUtils.isEmpty(param.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("skuTitle",param.getKeyword()));
            //构建高亮
            HighlightBuilder builder = new HighlightBuilder();
            builder.field("skuTitle");
            builder.preTags("<b style='color:red'>");
            builder.postTags("</b>");
            sourceBuilder.highlighter(builder);
        }
        // 1.2 filter - 按照三级分类id查询
        if (param.getCatalog3Id() != null){
            boolQuery.filter(QueryBuilders.termQuery("catalogId",param.getCatalog3Id()));
        }
        // 1.3 filter - 按照品牌id查询
        if (param.getBrandId() != null && param.getBrandId().size() > 0){
            boolQuery.filter(QueryBuilders.termQuery("brandId",param.getBrandId()));
        }
        // 1.4 按照指定的属性查询
        if (param.getAttrs() != null && param.getAttrs().size() > 0){
            List<String> attrs = param.getAttrs();
            //attr = 1_5寸：16存  9_8G:9G
            for (String attr : attrs) {
                //构建bool查询
                BoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery();
                String[] idAndAttr = attr.split("_");
                nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrId",idAndAttr[0]));
                String[] attrValues = idAndAttr[1].split(":");
                //TODO 我感觉这里会出问题
                nestedBoolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));
                //构建嵌入式查询，将bool查询条件传入，且这个条件不参与评分
                NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs",nestedBoolQuery, ScoreMode.None);
                boolQuery.filter(nestedQuery);
            }
        }
        // 1.5 根据是否有库存查询，默认查询所有，数值为1时只查询有库存的
        if (param.getHasStock() == 1) {
            boolQuery.filter(QueryBuilders.termQuery("hasStock", true));
        }
        // 1.6 查询价格区间
        if (!StringUtils.isEmpty(param.getSkuPrice())){
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
            //三种输入 100_500  200_  _200，价格区间
            String range = param.getSkuPrice();
            if (range.startsWith("_")){ //只有上限
                String max = range.substring(range.indexOf("_") + 1, range.length());
                rangeQuery.lte(max);    //设置上限
            }else {
                String[] split = range.split("_");  //将字符串以 _ 分割
                if (split.length == 2){
                    rangeQuery.gte(split[0]).lte(split[1]); //在一个价格区间之间
                }else {
                    rangeQuery.gte(split[0]);   //只有下限
                }
            }
            boolQuery.filter(rangeQuery);
        }
        /*-------------------------------------查询end--------------------------------------*/

        /*-------------------------------------排序end--------------------------------------*/
        /**
         *   排序，分页
         */
        // 2.1 排序
        if (!StringUtils.isEmpty(param.getSort())){
            String sort = param.getSort();
            //sort = saleCount_asc/desc
            String[] sortSpilt = sort.split("_");
            sourceBuilder.sort(sortSpilt[0],SortOrder.fromString(sortSpilt[1]));
        }
        // 2.2 分页
        Integer currentPage = param.getPageNum();
        int begin = (currentPage - 1) * EsConstant.PRODUCT_PAGESIZE;    //计算开始的索引
        sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);    //数据大小
        sourceBuilder.from(begin);
        /*-------------------------------------排序end--------------------------------------*/

        /*-------------------------------------聚合分析begin--------------------------------------*/
        /**
         *   聚合分析
         */
        // 3.1 品牌聚合
        TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brandAgg");
        brandAgg.field("brandId");  //没设置大小
        brandAgg.subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName"));
        brandAgg.subAggregation(AggregationBuilders.terms("brandImgAgg").field("brandImg"));
        sourceBuilder.aggregation(brandAgg);
        // 3.2 分类聚合
        TermsAggregationBuilder catalogAgg = AggregationBuilders.terms("catalogAgg");
        catalogAgg.field("catalogId");
        catalogAgg.subAggregation(AggregationBuilders.terms("catalogNameAgg").field("catalogName"));
        sourceBuilder.aggregation(catalogAgg);
        // 3.3 属性聚合
        NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");    //嵌入式聚合
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId");   //根据AttrId聚合

        attrIdAgg.subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName")); //AttrId的子聚合
        attrIdAgg.subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue"));   //AttrId的子聚合

        attr_agg.subAggregation(attrIdAgg);
        sourceBuilder.aggregation(attr_agg);
        /*-------------------------------------聚合分析end--------------------------------------*/

        System.out.println("构建的DSL ：" + sourceBuilder.toString());
        SearchRequest request = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);
        return request;
    }
}
